﻿
package extremefx.data {
	import extremefx.IDisposable;
	import extremefx.IEnumerable;
	import extremefx.IEnumerator;
	import extremefx.collections.GenericEnumerator;
	import extremefx.collections.ICollection;
	import extremefx.events.EventHandler;
	import extremefx.extensions.isNullOrEmpty;
	import extremefx.tools.getFunctionByName;

	import flash.net.registerClassAlias;
	import flash.utils.IDataInput;
	import flash.utils.IDataOutput;

	/**
	 * @author Marcelo Volmaro
	 */
	public class DataSource implements IEnumerable, ICollection, IDisposable {
		private static const rgc:* = registerClassAlias("extremefx.data.DataSource", DataSource);
		private static const _format:Function = getFunctionByName("extremefx.i18.XString", "format"); 

		private var _hasDS:Boolean;
		private var _itemList:IEnumerator;
		private var _textField:String;
		private var _keyField:String;
		private var _tfFormat:String;
		private var _dataObject:Vector.<DSItem>;

		public function DataSource(pDataSource:*, pDataTextField:String = null, pDataKeyField:String = null, pDataTextFieldFormat:String = null) {
			setData(pDataSource, pDataTextField, pDataKeyField, pDataTextFieldFormat);
		}
		
		private var _disposing:Boolean;

		public function setData(pDataSource:*, pDataTextField:String = null, pDataKeyField:String = null, pDataTextFieldFormat:String = null):void {
			_itemList = GenericEnumerator.getEnumerator(pDataSource);
			_hasDS = (_itemList is IEnumerator);
			if (!_hasDS){
				_dataObject = new Vector.<DSItem>();
			}
			
			_textField = pDataTextField;
			_keyField = pDataKeyField;
			_tfFormat = pDataTextFieldFormat;
			_dataBind();
		}
		
		public function get hasValidData():Boolean {
			return _hasDS;
		}
		
		public function getEnumerator():IEnumerator {
			return new VectorEnum(_dataObject);
		}

		private var _eOnDataChange:EventHandler;
		public function get onDataChange():EventHandler{
			if (_eOnDataChange == null) _eOnDataChange = new EventHandler(this);
			return _eOnDataChange;
		}
		
		public function toArray():Array {
			return _dataObject as Array;
		}
		
		public function readExternal(input:IDataInput):void {
			_textField = input.readUTF();
			_keyField = input.readUTF();
			_tfFormat = input.readUTF();
			_hasDS = input.readBoolean();
			_itemList = input.readObject();
		}
		
		public function writeExternal(output:IDataOutput):void {
			output.writeUTF(_textField);
			output.writeUTF(_keyField);
			output.writeUTF(_tfFormat);
			output.writeBoolean(_hasDS);
			output.writeObject(_itemList);
			rgc;
		}
		
		public function get count():uint {
			return _dataObject.length;
		}
		
		public function getItemAt(pPos:uint):DSItem {
			return ((pPos < _dataObject.length) ? _dataObject[pPos] : null);
		}

		private function _dataBind():void {
			if (_hasDS){
				var text:String, 
					data:String,
					hasText:Boolean = !isNullOrEmpty(_textField),
					hasKey:Boolean = !isNullOrEmpty(_keyField),
					hasTFormat:Boolean = (!isNullOrEmpty(_tfFormat) && _format != null),
					obj:*,
					d:Vector.<DSItem> = new Vector.<DSItem>(),
					i:uint = 0;

				while (_itemList.moveNext()){
					obj = _itemList.current;
					text = null; 
					data = null;
					
					if (hasText){
						text = obj[_textField];
						if (hasTFormat) text = _format(_tfFormat, text);
					}
					
					if (hasKey){
						data = obj[_keyField].toString();
						
					} else if (!hasText){
						text = data = obj.toString();
						if (hasTFormat) text = _format(_tfFormat, text);
						
					} else if (text){
						data = text;
					}
					
					d[i++] = new DSItem(text, data, obj, i);
				}
				
				_dataObject = d;
				
				if (_eOnDataChange) _eOnDataChange.fire();
			}
		}
		
		public function dispose():void {
			if (_disposing) return;
			_disposing = true;
			
			_eOnDataChange.dispose();
		}
	}
}

import extremefx.IEnumerator;
import extremefx.data.DSItem;

final class VectorEnum implements IEnumerator {
	private var _array:Vector.<DSItem>;
	private var _idx:int;
	private var _len:int;
	
	public function VectorEnum(pA:Vector.<DSItem>) {
		_array = pA;
		reset();
	}
	
	public function moveNext():Boolean {
		return (++_idx) < _len;
	}
	
	public function reset():void {
		_idx = -1;
		_len = _array.length;
	}
	
	public function get current():* {
		return _array[_idx];
	}
}